package client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SealedObject;

import messages.Message;
import messages.encrypted.EncryptedMessage;
import messages.plain.PlainAuthRequest;
import messages.plain.PlainAuthResponse;
import messages.plain.PlainAuthResponseAck;
import messages.plain.PlainMessage;
import messages.plain.TextMessage;
import utils.ClientAuthState;

/**
 * client thread listens to incoming messages.
 * 
 * @author bh349
 */
public class ClientThread extends Thread
{
    private Client client;

    private Socket socket;

    private ObjectInputStream in;

    public ClientThread( Client client ) throws IOException {
        this.client = client;
    }

    public void run() {
        try {
            socket = client.serverSocket.accept();
            in = new ObjectInputStream( socket.getInputStream() );
        } catch( IOException e1 ) {
            e1.printStackTrace();
        }

        Message msg;
        try {
            while( (msg = (Message) in.readObject()) != null ) {
                System.out.println( "Current Auth State: " + client.authState );
                PlainMessage pMessage = null;
   //             boolean fromSealed = false;
                if( msg instanceof EncryptedMessage ) {
                    System.out.println( "Received An Encrypted Message" );
                    msg.show();
                    EncryptedMessage encMsg = (EncryptedMessage) msg;
                    SealedObject sObj = encMsg.getsObj();
                    Cipher cipher = Cipher.getInstance( "RSA" );
                    cipher.init( Cipher.DECRYPT_MODE, client.privateKey );
                    pMessage = (PlainMessage) sObj.getObject( cipher );
 //                   fromSealed = true;
                }
                
                if (pMessage==null)
                    pMessage = (PlainMessage) msg;
                
                if( pMessage instanceof PlainMessage ) {
                    pMessage = (PlainMessage) pMessage;
                    System.out.println( "Received An UnEncrypted Message" );
                    if( pMessage instanceof PlainAuthRequest
                            && (client.authState
                                    .equals( ClientAuthState.NO_AUTH ) || client.authState
                                    .equals( ClientAuthState.AUTHREQ_SENT )) ) {
                        System.out.println( "It is a valid message 1" );
                        pMessage.show();
                        client.authState = ClientAuthState.AUTHRESP_SENT;
                    } else if( pMessage instanceof PlainAuthResponse
                            && client.authState
                                    .equals( ClientAuthState.AUTHREQ_SENT ) ) {
                        System.out.println( "This is message 2." );
                        PlainAuthResponse resp = (PlainAuthResponse) pMessage;
                        pMessage.show();
                        if( resp.getDstNonce() == client.nonce
                                  ) {
                            System.out
                                    .println( "Valid message 2. Authentication Done! Current state: AUTH." );
                            client.authState = ClientAuthState.AUTH;
                        } else {
                            System.out
                                    .println( "This message contains bad nonce. This message has been ignored." );
                            client.authState = ClientAuthState.NO_AUTH;
                        }

                    } else if( pMessage instanceof PlainAuthResponseAck
                            && client.authState
                                    .equals( ClientAuthState.AUTHRESP_SENT ) ) {
                        System.out.println( "This is message 3." );
                        PlainAuthResponseAck ack = (PlainAuthResponseAck) pMessage;
                        ack.show();
                        if( ack.getDstNonce() == client.nonce
                                 ) {
                            System.out
                                    .println( "Valid message 2. Authentication Done! Current state: AUTH." );
                            client.authState = ClientAuthState.AUTH;
                        } else {
                            System.out
                                    .println( "This message contains bad nonce. This message has been ignored." );
                            client.authState = ClientAuthState.NO_AUTH;
                        }

                    } else if( pMessage instanceof TextMessage ) {
                        TextMessage text = (TextMessage) pMessage;
                        text.show();
                    } else {
                        System.out
                                .println( "You received a message in an inproper state. (Maybe Auth is already done.)" );
                    }
                }
            }
        } catch( IOException e ) {
            e.printStackTrace();
        } catch( ClassNotFoundException e ) {
            e.printStackTrace();
        } catch( IllegalBlockSizeException e ) {
            e.printStackTrace();
        } catch( BadPaddingException e ) {
            e.printStackTrace();
        } catch( InvalidKeyException e ) {
            e.printStackTrace();
        } catch( NoSuchAlgorithmException e ) {
            e.printStackTrace();
        } catch( NoSuchPaddingException e ) {
            e.printStackTrace();
        }
    }
}
